import service
import uthread
import util
import log
import _weakref
import uiutil
import uiconst
import uicls
import base
import blue

class UIEventSvc(service.Service):
    __update_on_reload__ = 1
    __guid__ = 'svc.event'

    def Run(self, *etc):
        service.Service.Run(self, *etc)
        self._triuiRegs = {}
        self._unresolvedKeyDown = None
        self._lastKeyDown = None
        self.appfocusitem = None
        self.charsThatCanBlock = set()
        self.modkeysOff = tuple([ 0 for x in uiconst.MODKEYS ])



    def InitAccelerators(self):
        self.ResetTriuiEventHandling()
        if uicore.newRendererEnabled:
            return 
        self.RegisterForTriuiEvents([uiconst.UI_ACTIVE], self.CheckAppFocus)
        self.RegisterForTriuiEvents([uiconst.UI_MOUSEDOWN], self.OnMouseDown_TriuiCallback)
        self.RegisterForTriuiEvents([uiconst.UI_MOUSEENTER], self.OnMouseEnter_TriuiCallback)
        self.RegisterForTriuiEvents([uiconst.UI_MOUSEEXIT], self.OnMouseExit_TriuiCallback)
        self.RegisterForTriuiEvents([uiconst.UI_MOUSEMOVE], self.OnMouseMove_TriuiCallback)
        uicore.uilib.wheelHandler = self.OnWheel
        uicore.cmd.Reload()



    def OnMouseDown_TriuiCallback(self, downOn, *args):
        if not uiutil.IsUnder(downOn, uicore.layer.menu):
            uiutil.Flush(uicore.layer.menu)
        currentFocus = uicore.registry.GetFocus()
        if (currentFocus != downOn):
            uicore.registry.SetFocus(downOn)
        return True



    def OnMouseExit_TriuiCallback(self, exitFrom, *args):
        self.CheckWindowEnterExit()
        return True



    def OnMouseMove_TriuiCallback(self, movingOn, *args):
        uicore.CheckHint()
        self.CheckWindowEnterExit()
        return True



    def OnMouseEnter_TriuiCallback(self, enterOn, *args):
        uicore.CheckHint()
        uicore.CheckCursor()
        self.CheckWindowEnterExit()
        return True



    def CheckWindowEnterExit(self):
        item = uicore.uilib.mouseOver
        while item.parent:
            if (isinstance(item, uicls.WindowCore) and (item.state == uiconst.UI_NORMAL)):
                item.ShowHeaderButtons()
                break
            if not item.parent:
                break
            item = item.parent




    def CheckAppFocus(self, wnd, msgID, vkey):
        if (getattr(uicore, 'registry', None) is None):
            return 
        else:
            uicore.UpdateCursor(uicore.uilib.mouseOver, 1)
            focused = vkey[0]
            if (focused and (self.appfocusitem and self.appfocusitem())):
                f = self.appfocusitem()
                if ((f is not None) and (not f.destroyed)):
                    uicore.registry.SetFocus(f)
                self.appfocusitem = None
            focus = uicore.registry.GetFocus()
            if ((not focused) and (uicore.uilib and focus)):
                self.appfocusitem = _weakref.ref(focus)
                uicore.registry.SetFocus(None)
            return 1



    def OnWheel(self, dz):
        focus = uicore.registry.GetFocus()
        if ((not focus) or (getattr(focus, '_OnMouseWheel', None) is None)):
            mo = uicore.uilib.mouseOver
            while not getattr(mo, '_OnMouseWheel', None):
                if ((not mo.parent) or (mo == uicore.uilib.desktop)):
                    break
                mo = mo.parent

            if getattr(mo, '_OnMouseWheel', None):
                return mo._OnMouseWheel()



    def CheckAccelerators(self, vkey, flag):
        modkeys = self.GetModifierKeyState(vkey)
        if self.CheckMappedAccelerators(modkeys, vkey, flag):
            return True
        else:
            if self.CheckDirectionalAccelerators(vkey):
                return True
            return False



    def GetModifierKeyState(self, vkey = None):
        ret = []
        for key in uiconst.MODKEYS:
            ret.append((uicore.uilib.Key(key) and (key != vkey)))

        return tuple(ret)



    def CheckMappedAccelerators(self, modkeys, vkey, flag):
        ctrl = uicore.uilib.Key(uiconst.VK_CONTROL)
        if ((not ctrl) and ((self.modkeysOff, vkey) in uicore.cmd.commandMap.accelerators)):
            cmd = uicore.cmd.commandMap.accelerators[(self.modkeysOff, vkey)]
            if cmd.ignoreModifierKey:
                if ((not cmd.repeatable) and (flag & 1073741824)):
                    return False
                try:
                    sm.ScatterEvent('OnCommandExecuted', cmd.name)
                    ret = cmd.callback()
                    if ret:
                        return ret
                except:
                    log.LogException(channel=self.__guid__)
        if ((modkeys, vkey) in uicore.cmd.commandMap.accelerators):
            cmd = uicore.cmd.commandMap.accelerators[(modkeys, vkey)]
            if ((not cmd.repeatable) and (flag & 1073741824)):
                return False
            try:
                sm.ScatterEvent('OnCommandExecuted', cmd.name)
                return cmd.callback()
            except:
                log.LogException(channel=self.__guid__)
        return False



    def CheckDirectionalAccelerators(self, vkey):
        active = uicore.registry.GetActive()
        focus = uicore.registry.GetFocus()
        focusOrActive = (focus or active)
        if ((vkey == uiconst.VK_UP) and hasattr(focusOrActive, 'OnUp')):
            uthread.pool('uisvc::CheckDirectionalAccelerators OnUp', focusOrActive.OnUp)
            return True
        if ((vkey == uiconst.VK_DOWN) and hasattr(focusOrActive, 'OnDown')):
            uthread.pool('uisvc::CheckDirectionalAccelerators OnDown', focusOrActive.OnDown)
            return True
        if ((vkey == uiconst.VK_LEFT) and hasattr(focusOrActive, 'OnLeft')):
            uthread.pool('uisvc::CheckDirectionalAccelerators OnLeft', focusOrActive.OnLeft)
            return True
        if ((vkey == uiconst.VK_RIGHT) and hasattr(focusOrActive, 'OnRight')):
            uthread.pool('uisvc::CheckDirectionalAccelerators OnRight', focusOrActive.OnRight)
            return True
        if ((vkey == uiconst.VK_HOME) and hasattr(focusOrActive, 'OnHome')):
            uthread.pool('uisvc::CheckDirectionalAccelerators OnHome', focusOrActive.OnHome)
            return True
        if ((vkey == uiconst.VK_END) and hasattr(focusOrActive, 'OnEnd')):
            uthread.pool('uisvc::CheckDirectionalAccelerators OnEnd', focusOrActive.OnEnd)
            return True



    def RegisterForTriuiEvents(self, msgIDlst, function, *args, **kw):
        if (type(msgIDlst) == int):
            msgIDlst = [msgIDlst]
        self.InitTriuiEventHandling()
        cookie = (uthread.uniqueId() or uthread.uniqueId())
        self._triuiRegs[cookie] = msgIDlst
        ref = util.CallableWeakRef(function)
        for id_ in msgIDlst:
            self._triuiRegsByMsgID.setdefault(id_, {})[cookie] = (ref,
             args,
             kw)

        self.LogInfo('RegisterForTriuiEvents', cookie, msgIDlst, function, args, kw)
        return cookie



    def UnregisterForTriuiEvents(self, cookie):
        if (cookie not in self._triuiRegs):
            return 
        self.LogInfo('UnregisterForTriuiEvents', cookie)
        try:
            for msgID_ in self._triuiRegs[cookie]:
                del self._triuiRegsByMsgID[msgID_][cookie]

            del self._triuiRegs[cookie]
        except KeyError, what:
            self.LogError('UnregisterForTriuiEvents: Tried to kill unexisting registration?', cookie)
            log.LogException()



    def RegisterCharsThatCanBlock(self, vkey, *args):
        self.charsThatCanBlock.add(vkey)



    def CheckKeyDown(self):
        blue.pyos.synchro.Yield()
        if self._unresolvedKeyDown:
            (vkey, flag,) = self._unresolvedKeyDown[-1]
            self._unresolvedKeyDown = None
            self.CheckAccelerators(vkey, flag)



    def OnTriuiEvent(self, wnd, msgID, param):
        if (getattr(self, '_triuiRegsByMsgID', None) is None):
            return 
        if (uicore.uilib is None):
            return 
        if (msgID == uiconst.UI_KEYDOWN):
            if self._unresolvedKeyDown:
                (_vkey, _flag,) = self._unresolvedKeyDown[-1]
                self._unresolvedKeyDown = None
                self.CheckAccelerators(_vkey, _flag)
            self._unresolvedKeyDown = (wnd,
             msgID,
             param)
            self._lastKeyDown = param
            uthread.new(self.CheckKeyDown)
        elif (msgID == uiconst.UI_CHAR):
            (char, flag,) = param
            ignoreChar = False
            (lVKey, lFlag,) = self._lastKeyDown
            if (char <= 32):
                ctrl = uicore.uilib.Key(uiconst.VK_CONTROL)
                if ((char not in (uiconst.VK_RETURN,
                 uiconst.VK_BACK,
                 uiconst.VK_SPACE)) or ctrl):
                    ignoreChar = True
            elif (lVKey in self.charsThatCanBlock):
                alt = uicore.uilib.Key(uiconst.VK_MENU)
                shift = uicore.uilib.Key(uiconst.VK_SHIFT)
                ctrl = uicore.uilib.Key(uiconst.VK_CONTROL)
                function = uicore.cmd.GetFuncByShortcut((ctrl,
                 alt,
                 shift,
                 lVKey))
                if (function is not None):
                    ignoreChar = True
            if not ignoreChar:
                charUsed = self.ResolveOnChar(char, flag)
                if charUsed:
                    self._unresolvedKeyDown = None
        elif (msgID == uiconst.UI_KEYUP):
            (vkey, flag,) = param
            if (vkey == uiconst.VK_SNAPSHOT):
                try:
                    uicore.cmd.PrintScreen()
                except:
                    pass
        elif ((msgID == uiconst.UI_MOUSEDOWN) and (param[0] > 1)):
            (mouseBtnNum, flag,) = param
            mouseBtn = {2: uiconst.VK_MBUTTON,
             3: uiconst.VK_XBUTTON1,
             4: uiconst.VK_XBUTTON2}.get(mouseBtnNum)
            if mouseBtn:
                self.CheckAccelerators(mouseBtn, flag)
        for (cookie, (wr, args, kw,),) in self._triuiRegsByMsgID.get(msgID, {}).items():
            try:
                if ((not wr()) or (not wr()(*(args + (wnd,
                 msgID,
                 param)), **kw))):
                    self.UnregisterForTriuiEvents(cookie)
            except UserError, what:

                def f():
                    raise what


                uthread.new(f)
            except:
                self.LogError('OnTriuiEvent (cookie', cookie, '): Exception when trying to process callback!')
                log.LogException()




    def ResolveOnChar(self, char, flag):
        ignore = (uiconst.VK_RETURN, uiconst.VK_BACK)
        if ((char < 32) and (char not in ignore)):
            return False
        focus = uicore.registry.GetFocus()
        if (focus and (hasattr(focus, 'OnChar') and uiutil.IsVisible(focus))):
            if not blue.win32.IsTransgaming():
                capsLock = uicore.uilib.GetKeyState(uiconst.VK_CAPITAL)
                if capsLock:
                    char = ord(unichr(char).upper())
            ret = focus.OnChar(char, flag)
            if ret:
                return True



    def InitTriuiEventHandling(self):
        if (self._triuiRegs == {}):
            self.ResetTriuiEventHandling()



    def ResetTriuiEventHandling(self):
        self._triuiRegs = {}
        self._triuiRegsByMsgID = {}
        uicore.uilib.globalHandler = [self.OnTriuiEvent,
         uiconst.UI_MOUSEDOWN,
         uiconst.UI_MOUSEUP,
         uiconst.UI_MOUSEEXIT,
         uiconst.UI_MOUSEENTER,
         uiconst.UI_MOUSEMOVE,
         uiconst.UI_CLICK,
         uiconst.UI_STARTDRAG,
         uiconst.UI_ENDDRAG,
         uiconst.UI_MOUSEHOVER,
         uiconst.UI_KEYDOWN,
         uiconst.UI_KEYUP,
         uiconst.UI_ACTIVE,
         uiconst.UI_CHAR]




